iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
自我挑戰組

30天深入淺出Redux系列 第 27

Redux 深入淺出 - [ Day 27 ] React RTK Typescript ExtraReducer

  • 分享至 

  • xImage
  •  

前一篇我們完成了一個基本的 counter slice 和簡單的 reducer,今天我們試做 extraReducer 的部分,一樣我們簡單設定一個計分的 slice 透過綁定 counter 的 minus & plus 來做加分的動作,當然你也可以按照自己的喜好更改,那麼我們就開始吧!

首先我們新增一個 pointSlice 的檔案於 slices 資料夾內,如下:

// src/features/slices/pointSlice.ts
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
import { minus, plus } from "./counterSlice";

interface PointState {
  pt: number;
}

const initialState: PointState = {
  pt: 0
}

const pointSlice = createSlice({
  name: 'point',
  initialState,
  reducers: {},
	// 綁定 counter 的 actions 與之前的做法一致
  extraReducers: (builder) => {
    builder
    .addCase(minus, (state) => {
      state.pt += 2
      return state
    })
    .addCase(plus, (state) => {
      state.pt += 3
      return state
    })
  }
})

export const selectPoint = (state: RootState) => state.point

export default pointSlice.reducer;

完成以上基本就是做些微的調整幾乎就完成了,那麼我們接著調整 store 的部分,如下:

// src/features/store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "./slices/counterSlice";
import pointSlice from "./slices/pointSlice";

const store = configureStore({
  reducer: {
    counter: counterSlice,
    point: pointSlice,
  },
})
// RootState要記得下export來提供slices使用
export type RootState = ReturnType<typeof store.getState>

export default store;

接著我們將分數顯示在畫面上,我們另外做一個 component 叫 Point,如下:

// src/components/Point.tsx
import { useSelector } from "react-redux"
import { selectPoint } from "../features/slices/pointSlice"

const Point = () => {
  const point = useSelector(selectPoint)
	// 這裡簡單引入就好,因為我們值的變更是綁定 counter
  return (
    <div className="card" style={{margin: '1rem 0'}}>
      <h5>{point.pt}</h5>
    </div>
  )
}

export default Point

接著,於 App 引入我們剛剛製作完成的 point component:

// src/App.tsx
import Counter from "./components/Counter"
import Point from "./components/Point"

function App() {
  return (
    <div className="container">
      <h1>Typescript Demo</h1>
      <Counter/>
      <Point/>
    </div>
  )
}

export default App

這時候畫面上的分數應該就會隨著你案的按鈕增加分數了,那麼到這裡基本的應用已經完成了,讓我們回過頭來處理 RTK query 的部分吧!

首先,移動到你 server 的資料夾下面,並啟動看看,我的話是預設在 port:8080,這部分的示範會以先前分享給各位的 nest todo Api server 為主,如果不想太麻煩的話可以考慮用 JSON server 去做簡單的 todo mock data 就好,那麼接下來的內容會簡單介紹一下 nest 裏面的內容。

https://pbs.twimg.com/profile_images/1110148780991623201/vlqCsAVP_400x400.png

在 nest 的結構裡面其實與 angular 的寫法是類似的,在 src/main.ts 為預設的入口,然後引入 app.module.ts 內的 AppModule 於 nest 框架核心 NestFactory 進行處理,在 app.module.ts 裏面會看到對應的 TodosModule 透過 todo.module.ts 裏面又可以看到另外引入的 controller,最後你會發現我們的 restApi 就是在 todo.controller.ts 進行撰寫的。

如果想要更深入理解 nest 的話持續關注我,也許明年或是之後有時間會出一系列文章介紹這個框架的用法,當然也可以直接造訪他的官方網站試試。

那麼透過 postman 或是其他你常用的工具測試看看拿到的資料結構會與 todo.controller.ts 內的結構相同,如下:

// src/modules/todos/todo.controller.ts
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  HttpCode,
} from '@nestjs/common';

interface Todo {
  id: number;
  text: string;
  active: boolean;
  done: boolean;
}

let todos: Todo[] = [
  'NestJS',
  'GraphQL',
  'Apollo',
  'TypeScript',
  'React',
  'Redux',
  'React Query',
  'Angular',
  'Vue',
  'D3',
  'Svelte',
  'SolidJS',
  'NextJS',
  'AWS',
].map((text, index) => ({
  id: index + 1,
  text: `Learn ${text}`,
  active: true,
  done: false,
}));

@Controller('todos')
export class TodosController {
  constructor() {}

  @Get()
  async index(): Promise<Todo[]> {
    return todos.filter(({ active }) => active);
  }

  @Get(':id')
  async show(@Param('id') id: string): Promise<Todo> {
    return todos.find((todo) => todo.id === parseInt(id));
  }

  @Post()
  async create(@Body() { text }: { text: string }): Promise<Todo> {
    const todo = {
      id: todos.length + 1,
      text,
      active: true,
      done: false,
    };
    todos.push(todo);
    return todo;
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() data: Todo): Promise<Todo> {
    todos = todos.map((todo) =>
      todo.id === parseInt(id) ? { ...todo, ...data } : todo,
    );

    return data;
  }

  @Delete(':id')
  @HttpCode(204)
  async destroy(@Param('id') id: string): Promise<number> {
    todos = todos.map((todo) =>
      todo.id === parseInt(id) ? { ...todo, active: false } : todo,
    );
    return parseInt(id);
  }
}

那麼知道了以上的資料結構,也確認了 server 是可以正常運行的話,下一篇我們就回歸正題來處理 RTK query 的部分了。


上一篇
Redux 深入淺出 - [ Day 26 ] React RTK Typescript 基本應用
下一篇
Redux 深入淺出 - [ Day 28 ] React RTK Query Typescript 實作
系列文
30天深入淺出Redux31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言